package com.googlecode.triples.resource;

import com.googlecode.triples.model.Triple;
import com.googlecode.triples.resource.model.Reference;
import com.googlecode.triples.resource.model.References;
import com.googlecode.triples.resource.model.Triples;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.ws.rs.core.PathSegment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

@Stateless
public class TripleQueryResourceBean implements TripleQueryResource {

	@PersistenceContext
	private EntityManager entityManager;

	public TripleQueryResourceBean() {
	}

	public TripleQueryResourceBean(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	// query/object//attribute

	public Triples find(String object, String attribute) {
		final List<Triple> list = findTriples(object, attribute);
		return new Triples(list);
	}

	public References findReferences(String object, String attribute) {
		@SuppressWarnings("unchecked")
		final List<Long> idList = entityManager.createNamedQuery("tripleIdsByObjectAttribute")
				.setParameter("object", object)
				.setParameter("attribute", attribute)
				.getResultList();
		final List<Reference> referenceList = new ArrayList<Reference>(idList.size());
		for (Long id : idList) {
			referenceList.add(new Reference(id));
		}
		return new References(referenceList);
	}

	public Long findCount(String object, String attribute) {
		return (Long) entityManager.createNamedQuery("tripleCountByObjectAttribute")
				.setParameter("object", object)
				.setParameter("attribute", attribute)
				.getSingleResult();
	}

	public String findValue(String object, String attribute) {
		return (String) entityManager.createNamedQuery("tripleValueByObjectAttribute")
				.setParameter("object", object)
				.setParameter("attribute", attribute)
				.getSingleResult();
	}

	public String findValues(String object, String attribute) {
		@SuppressWarnings("unchecked")
		final List<String> list = entityManager.createNamedQuery("tripleValueByObjectAttribute")
				.setParameter("object", object)
				.setParameter("attribute", attribute)
				.getResultList();
		final StringBuilder builder = new StringBuilder();
		for (String s : list) {
			builder.append(s).append("\n");
		}
		return builder.toString();
	}

	// query/object//attributes

	public Triples findRecursive(String object, PathSegment attributes) {
		final List<String> attributeList = attributes.getMatrixParameters().get("attribute");
		final Triple start = new Triple();
		start.setValue(object);
		List<Triple> currentTriples = Collections.singletonList(start);
		List<Triple> nextTriples = new LinkedList<Triple>();
		for (String attribute : attributeList) {
			for (Triple triple : currentTriples) {
				final List<Triple> tripleList = findTriples(triple.getValue(), attribute);
				nextTriples.addAll(tripleList);
			}
			currentTriples = nextTriples;
			nextTriples = new LinkedList<Triple>();
		}
		return new Triples(currentTriples);
	}

	public int findRecursiveCount(final String object, final PathSegment attributes) {
		return findRecursive(object, attributes).getList().size();
	}

	// query/all

	public Triples getAll() {
		@SuppressWarnings("unchecked")
		final List<Triple> list = entityManager.createNamedQuery("tripleAll").getResultList();
		return new Triples(list);
	}

	public References getAllReferences() {
		@SuppressWarnings("unchecked")
		final List<Long> idList = entityManager.createNamedQuery("tripleIds").getResultList();
		final List<Reference> list = new ArrayList<Reference>(idList.size());
		for (Long id : idList) {
			list.add(new Reference(id));
		}
		return new References(list);
	}

	public Long getAllCount() {
		return (Long) entityManager.createNamedQuery("tripleCount").getSingleResult();
	}

	// private methods

	@SuppressWarnings("unchecked")
	private List<Triple> findTriples(String object, String attribute) {
		return (List<Triple>) entityManager.createNamedQuery("tripleByObjectAttribute")
				.setParameter("object", object)
				.setParameter("attribute", attribute)
				.getResultList();
	}
}
